﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using BesAsm.Stormwater.HspfCore;
using BesAsm.Framework.Reporting;

namespace BesAsm.Stormwater.HspfResults
{
  public class L61Reader : IMessenger
  {
    private string _file;
    private LandSegmentsCollection _segments;
    public bool IsRead { get; private set; }

    public L61Reader(string file)
    {
      _segments = new LandSegmentsCollection();
      _file = file;
      IsRead = false;
      MessageQueue.AddMessenger(this);
    }

    public void Read(L61ResultsTimePeriod resultsTimePeriod)
    {
      StreamReader reader = new StreamReader(_file);
      List<string> blockLines = new List<string>();

      ReportMessage("Reading L61 file from '" + _file + "'", ReportableMessageType.Info);

      string line = reader.ReadLine();
      if (line != "1")
        throw new IOException("L61 File does not begin with a valid block");
      blockLines.Add(line);

      while (!reader.EndOfStream)
      {
        line = reader.ReadLine();

        if (IsNewBlock(line))
        {
          string header = blockLines[2];
          LandSegmentResults segment = ParseAndAddIfNew(header);

          L61Block results;
          results = ReadBlock(blockLines, segment);

          if (resultsTimePeriod == results.ResultsTimePeriod)
            segment.Add(results);

          blockLines.Clear();
        }
        blockLines.Add(line);
      }
      IsRead = true;
      return;
    }

    public LandSegmentsCollection LandSegments
    {
      get 
      {
        if (IsRead)
          return _segments;
        else
          throw new Exception("L61 Data has not been read");
      }
    }

    private bool IsNewBlock(string line)
    {
      return line == "1";
    }

    private L61Block ReadBlock(List<string> blockLines, ILandSegment segment)
    {
      L61Block block;

      switch (segment.Type)
      {
        case SegmentType.Impland:
          block = new L61ImplandBlock(segment);
          break;
        case SegmentType.Perland:
          block = new L61PerlandBlock(segment);
          break;
        default:
          throw new NotImplementedException();
      }

      block.AddBlockLines(blockLines);

      return block;
    }

    private LandSegmentResults ParseAndAddIfNew(string header)
    {
      int segmentNum = 0;
      string segmentName = String.Empty; ;
      SegmentType segmentType;

      string[] tokens = header.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

      if (tokens[0] == "PERVIOUS")
        segmentType = SegmentType.Perland;
      else if (tokens[0] == "IMPERVIOUS")
        segmentType = SegmentType.Impland;
      else
        throw new ArgumentException("Could not determine Segment Type");

      int nameStartIndex = 0;
      int nameEndIndex = 0;

      for (int i = 0; i < tokens.Length; i++)
      {
        if (tokens[i] == "NO.")
        {
          segmentNum = int.Parse(tokens[i + 1]);
          if (_segments.Contains(segmentNum))
            return (LandSegmentResults)_segments[segmentNum];

          nameStartIndex = i + 2;
        }
        if (tokens[i] == "REPORT")
          nameEndIndex = i;
      }

      for (int i = nameStartIndex; i < nameEndIndex; i++)
      {
        segmentName = segmentName + " " + tokens[i];
      }
      segmentName = segmentName.Trim();
      LandSegmentResults segmentResults = null;
      switch (segmentType)
      {
        case SegmentType.Perland:
          segmentResults =
            new PerlandSegmentResults(segmentNum, segmentName);
          break;
        case SegmentType.Impland:
          segmentResults =
            new ImplandSegmentResults(segmentNum, segmentName);
          break;
        default:
          throw new NotImplementedException();
      }

      ReportMessage("Added land segment " + segmentName, ReportableMessageType.Info);
      _segments.Add(segmentResults);

      return (LandSegmentResults)_segments[segmentNum];

    }

    #region IMessenger Members

    public event MessageReportedEventHandler MessageReported;

    /// <summary>
    /// Called when a message is to be reported.
    /// </summary>
    /// <param name="e">The message to report</param>
    protected void OnMessageReported(MessageReportedArgs e)
    {
      if (MessageReported != null)
        MessageReported(this, e);
    }

    protected void ReportMessage(string message, ReportableMessageType type)
    {
      ReportableMessage rm = new ReportableMessage()
      {
        MessageType = type,
        Description = message
      };
      OnMessageReported(new MessageReportedArgs(rm));
    }

    #endregion
  }

  public enum L61LineType
  {
    BlockStart, BlockHeader, StateHeader, StateBody,
    ExternalFlowHeader, ExternalFlowBody,
    EvapotranspirationHeader, EvapotranspirationBody,
    InternalFluxHeader, InternalFluxBody,
    BalanceHeader, BalanceBody,
    Unused
  }
}
